package io.hellsinger.vortex.ui.component.navigation

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Paint
import android.graphics.drawable.RippleDrawable
import android.text.TextUtils
import android.view.ViewGroup
import android.view.ViewPropertyAnimator
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import io.hellsinger.theme.Colors
import io.hellsinger.vortex.ui.component.ViewMeasure
import io.hellsinger.vortex.ui.component.children
import io.hellsinger.vortex.ui.component.dp
import io.hellsinger.vortex.ui.component.drawable.NavigationIconDrawable
import io.hellsinger.vortex.ui.component.drawable.NavigationIconDrawable.Type
import io.hellsinger.vortex.ui.component.drawable.RoundedCornerDrawable
import io.hellsinger.vortex.ui.component.item.menu.MenuAction
import io.hellsinger.vortex.ui.component.item.menu.MenuActionListener
import io.hellsinger.vortex.ui.component.item.menu.MenuLayout
import io.hellsinger.vortex.ui.dsl.frameParams

// TODO: Animate sliding title
class NavigationBarView(
    context: Context,
) : FrameLayout(context) {
    private val icon =
        NavigationIconDrawable().apply {
            type = Type.CLOSE
        }

    private var currentAnimator: ViewPropertyAnimator? = null
    var state = 0
        private set

    private val listeners = mutableListOf<StateListener>()

    private val horizontalPadding = 16.dp
    private val verticalPadding = 16.dp
    private val titleHorizontalPadding = 32.dp

    private val surface =
        RoundedCornerDrawable(Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG)).apply {
            setElevation(
                4F.dp,
            )
        }

    private val navigationIconView =
        ImageView(context).apply {
            isClickable = true
            isFocusable = true
            frameParams(24.dp, 24.dp)
            navigationIcon
            background =
                RippleDrawable(
                    ColorStateList.valueOf(
                        navigationIcon.getColor(),
                    ),
                    null,
                    null,
                )
            setImageDrawable(icon)
        }

    private val titleView = arrayOfNulls<TextView>(size = 2)
    private var titleColor: Int = Colors.White.toInt()

    private val subtitleView =
        TextView(context).apply {
            textSize = 14F
        }

    private val menuView =
        MenuLayout(context).apply {
        }

    val navigationIcon: NavigationIconDrawable
        get() = icon

    var animatesTitleChanges: Boolean = true
    var animatesSubtitleChanges: Boolean = true

    private var textWidth = 0

    var title: CharSequence?
        get() = titleView.getOrNull(0)?.text
        set(value) {
            ensureContainingTitle(0)
            if (titleView[0] == value) return
            titleView[0]?.text = value
        }

    var subtitle: CharSequence?
        get() = subtitleView.text
        set(value) {
            if (subtitle == value) return
            ensureContainingSubtitle()
            subtitleView.text = value
        }

    val containsTitle: Boolean
        get() = (titleView[0] ?: false) in children

    val containsSubtitle: Boolean
        get() = subtitleView in children

    val containsNavigationIcon: Boolean
        get() = navigationIconView in children

    val containsMenu: Boolean
        get() = menuView in children

    init {
        background = surface
        elevation = 4F.dp
        ensureContainingNavigationIcon()
    }

    private fun createTitle(index: Int) {
        titleView[index] =
            TextView(context).apply {
                textSize = 18F
                ellipsize = TextUtils.TruncateAt.END
                setLines(1)
                setTextColor(titleColor)
                layoutParams =
                    LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT,
                    )
            }
    }

    private fun ensureContainingTitle(index: Int) {
        if (!containsTitle) {
            createTitle(index)
            addView(titleView[index])
        }
    }

    private fun ensureContainingSubtitle() {
        if (!containsSubtitle) addView(subtitleView)
    }

    private fun ensureContainingNavigationIcon() {
        if (!containsNavigationIcon) addView(navigationIconView)
    }

    private fun ensureContainingMenu() {
        if (!containsMenu) addView(menuView)
    }

    fun setNavigationClickListener(listener: OnClickListener?) {
        navigationIconView.setOnClickListener(listener)
    }

    fun addStateListener(listener: StateListener) {
        listeners.add(listener)
    }

    fun removeStateListener(listener: StateListener) {
        listeners.remove(listener)
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()

        if (parent is ViewGroup) {
            (parent as ViewGroup).clipChildren = false
        }
    }

    fun show(animate: Boolean = true) {
        slideUp(animate)
    }

    fun hide(animate: Boolean = true) {
        slideDown(animate)
    }

    private fun slideUp(animate: Boolean = true) {
        if (state == STATE_SCROLLED_UP) return
        if (currentAnimator != null) {
            currentAnimator!!.cancel()
            clearAnimation()
        }
        state = STATE_SCROLLED_UP
        val targetTranslationY = 0
        if (animate) {
            animate()
                .setDuration(100L)
                .translationY(targetTranslationY.toFloat())
                .setUpdateListener { animator ->
                    listeners.forEach { listener -> listener.onSlide(animator.animatedFraction) }
                }.setListener(
                    object : AnimatorListenerAdapter() {
                        override fun onAnimationEnd(animation: Animator) {
                            currentAnimator = null
                        }
                    },
                ).start()
        } else {
            translationY = targetTranslationY.toFloat()
        }
    }

    private fun slideDown(animate: Boolean = true) {
        if (state == STATE_SCROLLED_DOWN) return
        if (currentAnimator != null) {
            currentAnimator!!.cancel()
            clearAnimation()
        }

        val targetTranslationY = height
        state = STATE_SCROLLED_DOWN
        listeners.forEach { listener -> listener.onStateChanged(state) }
        if (animate) {
            animate()
                .setDuration(100L)
                .translationY(targetTranslationY.toFloat())
                .setUpdateListener { animator ->
                    listeners.forEach { listener -> listener.onSlide(animator.animatedFraction) }
                }.setListener(
                    object : AnimatorListenerAdapter() {
                        override fun onAnimationEnd(animation: Animator) {
                            currentAnimator = null
                        }
                    },
                )
        } else {
            translationY = targetTranslationY.toFloat()
        }
    }

    fun addItem(action: MenuAction) {
        ensureContainingMenu()
        menuView.addItem(action)
    }

    fun replaceItems(actions: List<MenuAction>) {
        ensureContainingMenu()
        menuView.replaceItems(actions)
    }

    fun removeItem(action: MenuAction) {
        ensureContainingMenu()
        menuView.removeItem(action)
    }

    fun registerListener(listener: MenuActionListener) {
        menuView.addListener(listener)
    }

    fun unregisterListener(listener: MenuActionListener) {
        menuView.removeListener(listener)
    }

    override fun onMeasure(
        widthMeasureSpec: Int,
        heightMeasureSpec: Int,
    ) {
        ViewMeasure(
            widthSpec = widthMeasureSpec,
            heightSpec = heightMeasureSpec,
            desireWidth = ViewGroup.LayoutParams.MATCH_PARENT,
            desireHeight = 56,
        ) { width, height ->
            setMeasuredDimension(
                width - paddingLeft + paddingRight,
                height - paddingTop + paddingBottom,
            )

            if (containsNavigationIcon) {
                measureChild(
                    navigationIconView,
                    widthMeasureSpec,
                    heightMeasureSpec,
                )
            }

            if (containsTitle) {
                titleView.forEach { view ->
                    view?.let {
                        if (it.visibility != GONE) {
                            measureChild(
                                it,
                                widthMeasureSpec,
                                heightMeasureSpec,
                            )
                            it.pivotX = 0F
                            it.pivotY = 1F
                        }
                    }
                }
            }

            if (containsSubtitle) {
                measureChild(
                    subtitleView,
                    widthMeasureSpec,
                    heightMeasureSpec,
                )
            }

            if (containsMenu) {
                menuView.measure(
                    MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
                    56.dp,
                )
            }
        }
    }

    override fun onLayout(
        changed: Boolean,
        left: Int,
        top: Int,
        right: Int,
        bottom: Int,
    ) {
        super.onLayout(changed, left, top, right, bottom)
        var widthPosition = horizontalPadding
        var textHeightPosition = 8.dp

        if (containsNavigationIcon) {
            navigationIconView.layout(
                widthPosition,
                verticalPadding,
                widthPosition + navigationIconView.measuredWidth,
                verticalPadding + navigationIconView.measuredHeight,
            )
            widthPosition += navigationIconView.measuredWidth
        }

        if (containsNavigationIcon) widthPosition += titleHorizontalPadding
        if (!containsSubtitle) textHeightPosition *= 2

        if (containsTitle) {
            titleView.forEach { view ->
                view?.layout(
                    widthPosition,
                    textHeightPosition,
                    widthPosition + view.measuredWidth,
                    textHeightPosition + view.measuredHeight,
                )
            }

            textHeightPosition += titleView[0]?.measuredHeight ?: 0
        }

        if (containsSubtitle) {
            subtitleView.layout(
                widthPosition,
                textHeightPosition,
                widthPosition + subtitleView.measuredWidth,
                textHeightPosition + subtitleView.measuredHeight,
            )
        }

        widthPosition += textWidth

        if (containsMenu) {
            menuView.layout(
                0,
                0,
                menuView.measuredWidth,
                measuredHeight - paddingBottom,
            )
        }
    }

    fun setTitleWithAnimation(
        title: CharSequence?,
        isVertical: Boolean = true,
        isReverse: Boolean = false,
        duration: Long = 150L,
    ) {
        if (title == this.title) return
        val isCrossfade = title.isNullOrEmpty()

        if (isCrossfade) {
        }

        if (titleView[1]?.parent != null) {
            (titleView[1]?.parent as ViewGroup).removeView(titleView[1])
            titleView[1] = null
        }
        titleView[1] = titleView[0]
        titleView[0] = null
        this.title = title
        titleView[0]?.alpha = 0F

        val a: ViewPropertyAnimator? =
            titleView[0]?.animate()?.alpha(1f)?.setDuration(
                duration,
            )

        if (isVertical) {
            a?.translationY(0F)
        } else {
            a?.translationX(0F)
        }

        a?.start()

        val animator = titleView[1]?.animate()?.alpha(0F)
        if (!isCrossfade) {
            if (isVertical) {
                animator?.translationY(
                    if (isReverse) {
                        (-20F).dp
                    } else {
                        20F.dp
                    },
                )
            } else {
                animator?.translationX(
                    if (isReverse) {
                        (-20F).dp
                    } else {
                        20F.dp
                    },
                )
            }
        }
        animator?.setDuration(duration)?.setListener(
            object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    if (titleView[1]?.parent != null) {
                        (titleView[1]?.parent as ViewGroup).removeView(titleView[1])
                        titleView[1] = null
                    }

                    requestLayout()
                }
            },
        )
        animator?.start()
        requestLayout()
    }

    fun animateIconTo(type: Type) {
        icon.animateTo(type)
    }

    fun setTitleColor(color: Int) {
        titleColor = color
        titleView[0]?.setTextColor(color)
        titleView[1]?.setTextColor(color)
    }

    fun setSubtitleColor(color: Int) {
        subtitleView.setTextColor(color)
    }

    fun setIconsColor(color: Int) {
        navigationIcon.setColor(color)
        menuView.setItemColor(color)
    }

    fun setIconsRippleColor(color: Int) {
        (navigationIconView.background as RippleDrawable).setColor(ColorStateList.valueOf(color))
        menuView.setItemRippleColor(color)
    }

    override fun setBackgroundColor(color: Int) {
        surface.setColor(color)
    }

    interface StateListener {
        fun onSlide(factor: Float)

        fun onStateChanged(state: Int)
    }

    companion object {
        const val STATE_SCROLLED_DOWN = 1
        const val STATE_SCROLLED_UP = 2
    }
}
